package de.koller.reason.render.ray;

import de.koller.oceanFramework.math.RVec3;
import de.koller.oceanFramework.math.Vec3;
import de.koller.reason.render.obj.Polygon;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

public class Intersectioner {

	private Intersectioner() {}
	
	public static float intersection( RVec3 la, RVec3 lb, RVec3 planeO, RVec3 planeN ) {
		float numer = RVec3.dot( planeN, RVec3.difference( planeO, la ) );
		float denom = RVec3.dot( planeN, RVec3.difference( lb, la ) );
		
		return numer / denom;
	}
	
	public static RVec3 linePoint( RVec3 lineO, RVec3 lineN, float t ) {
		RVec3 cp = new RVec3();
		cp.x = lineO.x + lineN.x * t;
		cp.y = lineO.y + lineN.y * t;
		cp.z = lineO.z + lineN.z * t;
		return cp;
	}
	
	public static boolean isInPolygon( RVec3 p, RVec3 a, RVec3 b, RVec3 c ) {
		RVec3 v0 = RVec3.difference( c, a );
		RVec3 v1 = RVec3.difference( b, a );
		RVec3 v2 = RVec3.difference( p, a );

		float dot00 = RVec3.dot(v0, v0);
		float dot01 = RVec3.dot(v0, v1);
		float dot02 = RVec3.dot(v0, v2);
		float dot11 = RVec3.dot(v1, v1);
		float dot12 = RVec3.dot(v1, v2);

		float invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
		float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
		float v = (dot00 * dot12 - dot01 * dot02) * invDenom;

		return (u >= 0) && (v >= 0) && (u + v < 1);
	}

	public static RVec3 getRayTrace( RVec3 ra, RVec3 rb, RVec3 a, RVec3 b, RVec3 c, 
			boolean fullPlane, boolean endlessRay ) {
		RVec3 pn = RVec3.cross( RVec3.difference( b, a ), RVec3.difference( c, a ) ).normalize();
		RVec3 ln = RVec3.difference( rb, ra );
		
		float iv = intersection( ra, rb, a, pn );
		if( !endlessRay && (iv < 0 || iv > 1) )
			return null;
		
		RVec3 ip = linePoint( ra, ln, iv );
		
		if( fullPlane )
			return ip;
		
		if( isInPolygon( ip, a, b, c ) )
			return ip;
		else
			return null;
	}
	
	public static List<RayIntersection> cast( Ray ray, Set<Polygon> polygons ) {
		List<RayIntersection> intersections = new ArrayList<>();
		
		for( Polygon p : polygons ) {
			RVec3 i = getRayTrace( ray.a, ray.b, p.verteces[0].position, p.verteces[1].position, p.verteces[2].position, false, false );
			if( i == null )
				continue;
		
			float d = i.newInstance().sub( ray.a ).getLengthSquared();
			intersections.add( new RayIntersection( i, p, d ) );
		}
		
		Collections.sort( intersections );
		return intersections;
	}
	
	public static class RayIntersection implements Comparable<RayIntersection> {
		public final RVec3 intersectionPoint;
		public final Polygon polygon;
		
		private final float distanceToRayA;

		public RayIntersection( RVec3 intersectionPoint, Polygon polygon, float distanceToRayA ) {
			this.intersectionPoint = intersectionPoint;
			this.polygon = polygon;
			this.distanceToRayA = distanceToRayA;
		}

		@Override
		public int compareTo( RayIntersection o ) {
			return Float.compare( distanceToRayA, o.distanceToRayA );
		}
		
	}
}
